package cn.jtool.dig.common.netty.supports;

import io.netty.handler.codec.EncoderException;

import java.io.Serializable;
import java.io.UnsupportedEncodingException;

public class ByteBuffer implements Serializable {

    /**
     * 数据
     */
    private byte[] bytes;

    /**
     * 读取下标
     */
    private int readIndex;

    /**
     * 写入下标
     */
    private int writeIndex;

    public ByteBuffer(){
        this.bytes = new byte[8];
    }

    public ByteBuffer(byte[] bytes){
        this.bytes = bytes;
        this.readIndex = 0;
        this.writeIndex = this.bytes.length;
    }

    /**
     * 读取一个byte数据
     * @return
     */
    public int readByte(){
        this.checkRead(1);
        int result = Convert.byte2Int(ArraysUtils.subarrays(this.bytes, this.readIndex, 1), 1);
        this.readIndex++;
        return result;
    }

    /**
     * 读取一个word数据
     * @return
     */
    public int readWord(){
        this.checkRead(2);
        int result = Convert.byte2Int(ArraysUtils.subarrays(this.bytes, this.readIndex, 2), 2);
        this.readIndex = this.readIndex + 2;
        return result;
    }

    /**
     * 读取一个dwrod数据
     * @return
     */
    public long readDWord(){
        this.checkRead(4);
        long result = Convert.byte2Long(ArraysUtils.subarrays(this.bytes, this.readIndex, 4), 4);
        this.readIndex = this.readIndex + 4;
        return result;
    }

    /**
     * 读取一个GBK的字符串
     * @param length
     * @return
     */
    public String readGbkString(int length){
        return new String(this.readBytes(length));
    }

    /**
     * 读取一个字节数组
     * @param length
     * @return
     */
    public byte[] readBytes(int length){
        this.checkRead(length);
        byte[] result = ArraysUtils.subarrays(this.bytes, this.readIndex, length);
        this.readIndex = this.readIndex + length;
        return result;
    }

    /**
     * 读取所有剩余数据
     * @return
     */
    public byte[] readRemaining(){
        if(this.readIndex < this.bytes.length){
            return this.readBytes(this.bytes.length - this.readIndex);
        }
        return new byte[]{};
    }

    /**
     * 从首字节读取到写入的位置
     * @return
     */
    public byte[] readAll(){
        return ArraysUtils.subarrays(this.bytes, 0, this.writeIndex);
    }

    /**
     * 检查当前数据是否还可读取
     * @param length
     */
    private void checkRead(int length){
        if(this.readIndex + length > this.bytes.length){
            throw new IndexOutOfBoundsException("数据内容不足, 数据长度: "+this.bytes.length+", 当前读取位置:"+this.readIndex+", 本次需要读取:"+length);
        }
    }

    /**
     * 追加一个字节
     * @param b
     * @return
     */
    public ByteBuffer writeByte(byte b){
        return this.write(new byte[]{b});
    }

    public ByteBuffer writeByte(Integer b){
        return this.write(Convert.intTobytes(b, 1));
    }
    /**
     * 追一个WORD,无符号双字节整形,16位
     * @param word
     * @return
     */
    public ByteBuffer writeWord(Integer word){
        return this.write(Convert.intTobytes(word, 2));
    }

    /**
     * 追加一个DWORD,无符号四字节整形,32位
     * @param dword
     * @return
     */
    public ByteBuffer writeDWord(Long dword){
        return this.write(Convert.longTobytes(dword, 4));
    }

    /**
     * 追加Gbk编码的字符串
     * @param string
     * @return
     */
    public ByteBuffer writeGbkString(String string){
        try {
            return this.write(string.getBytes("gbk"));
        } catch (UnsupportedEncodingException e) {
            throw new EncoderException("编码异常");
        }
    }

    /**
     * 追加一个字节数组
     * @param bytes
     * @return
     */
    public ByteBuffer write(byte[] bytes){
        this.ensureCapacityInternal(bytes.length);
        ArraysUtils.arrayappend(this.bytes, this.writeIndex, bytes);
        this.writeIndex = this.writeIndex + bytes.length;
        return this;
    }

    /**
     * 检查数组是否够本次数据存放,如果不够则进行扩容
     * @param length
     */
    private void ensureCapacityInternal(int length){
        if(this.writeIndex + length > this.bytes.length * 0.8){
            byte[] tmp = this.bytes;
            int capacity = tmp.length;
            while (capacity - this.writeIndex < length) {
                capacity = capacity << 1;
            }
            this.bytes = new byte[capacity];
            System.arraycopy(tmp, 0, this.bytes, 0, tmp.length);
        }
    }
}
